﻿using System;
using System.Collections.Generic;
using System.Text;
using Xeml.Document.Contracts;
using Mpi.Common;
using Xeml.Document.Ontologies;
using System.Diagnostics;
using Xeml.Document.Manager;

namespace Xeml.Document
{
    public class DevelopmentalOntologyHandler : IPlainOntologyHandler, IHierarchicalView
    {
        public static readonly string URI = "http://www.plantontology.org/DevelopmentalStage";

        #region IPlainOntologyHandler Members

        private OntologyTermCollection _terms;
        public IEnumerable<IOntologyTerm> Terms
        {
            get { return _terms; }
        }

        public IOntologyTerm Find(Term t)
        {
            return _terms.Find(t.TermId);
        }

        public IOntologyTerm Find(string termId)
        {
            return _terms.Find(termId);
        }

        #endregion

        #region IOntologyHandler Members

        private string _nameSpace;
        public string NameSpace
        {
            get { return _nameSpace; }
        }

        private bool _autoValid;
        public bool AutoValidation
        {
            get
            {
                return _autoValid;
            }
            set
            {
                _autoValid = value;
            }
        }

        private string _instanceLocation;
        private bool _reloadNeeded;
        public string InstanceLocation
        {
            get
            {
                return _instanceLocation;
            }
            set
            {
                if (_instanceLocation != value)
                {
                    _instanceLocation = value;
                    _reloadNeeded = true;
                }
            }
        }

        private string _ontologyLoadMessage;
        public string OntologyLoadMessage
        {
            get { return _ontologyLoadMessage; }
        }

        public OntologyLoadState OntologyLoadState
        {
            get;
            private set;
        }

        public OntologyType OntologyType
        {
            get { return OntologyType.Developmental; }
        }

        private string _nameSpaceAlias;
        public void Load(string nameSpaceAlias)
        {
            Load(nameSpaceAlias, false);
        }

        private SimpleOboReader _oboReader;
        public void Load(string nameSpaceAlias, bool force)
        {
            if (_nameSpaceAlias != nameSpaceAlias)
            {
                _nameSpaceAlias = nameSpaceAlias;
                _reloadNeeded = true;
            }
            if (_reloadNeeded || force)
            {
                _terms.Clear();
                try
                {

                    string obo = OnlineCacheManager.Instance.DownloadContent(_instanceLocation);  //File.ReadAllText(_lastTempFile);
                    //File.Delete(_lastTempFile);
                    _oboReader.Read(obo);

                    if (_currentStanza != null && !_terms.Contains(_currentStanza.TermId))
                    {
                        _terms.Add(_currentStanza);
                    }

                    BuildHirachy();
                    _reloadNeeded = false;

                    OntologyLoadState = OntologyLoadState.Loaded;
                }
                catch (Exception ex)
                {
                    ReportError(ex);
                }
            }
        }

        void BuildHirachy()
        {

            List<IOntologyTerm> todo = new List<IOntologyTerm>();
            todo.AddRange(_terms);

            foreach (var term in _terms)
            {
                TermNode tn = TermNode.CreateNode(term);
                _nodes.Add(tn);
            }
            List<TermNode> processedNodes = new List<TermNode>();
            foreach (var tn in _nodes)
            {
                string id = tn.Term.TermId;
                if (tn.Term.Attributes.Contains("is_a") ||
                       tn.Term.Attributes.Contains("part_of") ||
                       tn.Term.Attributes.Contains("develops_from") ||
                       tn.Term.Attributes.Contains("relationship"))
                {
                    foreach (TermAttribute attr in tn.Term.Attributes)
                    {
                        string rel = "";
                        string val = "";
                        if (attr.Name == "is_a")
                        {
                            rel = "is_a";
                            val = attr.Value;
                            val = val.Split("!".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)[0].Trim();
                            var parent = _nodes.Find(x => x.Term.TermId == val);
                            if (parent != null)
                            {
                                parent.AddSubNode(tn, rel);
                                if (!processedNodes.Contains(tn))
                                    processedNodes.Add(tn);
                            }
                        }
                        if (attr.Name == "part_of")
                        {
                            rel = "part_of";
                            val = attr.Value;
                            val = val.Split("!".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)[0].Trim();
                            var parent = _nodes.Find(x => x.Term.TermId == val);
                            if (parent != null)
                            {
                                parent.AddSubNode(tn, rel);
                                if (!processedNodes.Contains(tn))
                                    processedNodes.Add(tn);
                            }
                        }
                        if (attr.Name == "develops_from")
                        {
                            rel = "develops_from";
                            val = attr.Value;
                            val = val.Split("!".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)[0].Trim();
                            var parent = _nodes.Find(x => x.Term.TermId == val);
                            if (parent != null)
                            {
                                parent.AddSubNode(tn, rel);
                                if (!processedNodes.Contains(tn))
                                    processedNodes.Add(tn);
                            }
                        }
                        if (attr.Name == "relationship")
                        {
                            rel = "relationship";
                            val = attr.Value;
                            val = val.Trim();
                            if (val.StartsWith("is_a"))
                            {
                                rel = "is_a";
                                val = val.Replace("is_a", "");
                                val = val.Trim();
                            }
                            if (val.StartsWith("part_of"))
                            {
                                rel = "part_of";
                                val = val.Replace("part_of", "");
                                val = val.Trim();
                            }
                            if (val.StartsWith("develops_from"))
                            {
                                rel = "develops_from";
                                val = val.Replace("develops_from", "");
                                val = val.Trim();
                            }
                            val = val.Split("!".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)[0].Trim();
                            var parent = _nodes.Find(x => x.Term.TermId == val);
                            if (parent != null)
                            {
                                parent.AddSubNode(tn, rel);
                                if (!processedNodes.Contains(tn))
                                    processedNodes.Add(tn);
                            }
                        }

                    }
                }
            }
            foreach (var tn in processedNodes)
            {
                _nodes.Remove(tn);
            }


        }

        public DevelopmentalOntologyHandler()
        {
            _terms = new OntologyTermCollection();
            _nodes = new List<TermNode>();
            _oboReader = new SimpleOboReader();
            _oboReader.NewStanza += new EventHandler<NewStanzaEventArgs>(_oboReader_NewStanza);
            _oboReader.NewTagValuePair += new EventHandler<NewAttributeEventArgs>(_oboReader_NewTagValuePair);
        }

        private void ReportError(Exception ex)
        {
            OntologyLoadState = OntologyLoadState.LoadFailed;
            _ontologyLoadMessage = ex.Message;
            Debug.WriteLine("DevelopmentalHandler. End ontology load with exception:" + ex.Message);
            ValidationMessage vm = new ValidationMessage(SeverityCode.Error, "Ontology file load failed: '" + ex.Message + "'", this.ComponentName, false);
            OnValidation(vm);
        }

        private void _oboReader_NewTagValuePair(object sender, NewAttributeEventArgs e)
        {

            if (e.Attribute == "default-namespace")
                _nameSpace = e.AttributeValue;

            if (_currentStanza != null)
            {
                switch (e.Attribute)
                {
                    case "id":
                        {
                            _currentStanza.TermId = e.AttributeValue;
                            break;
                        }
                    case "name":
                        {
                            _currentStanza.Name = e.AttributeValue;
                            break;
                        }
                    case "def":
                        {
                            _currentStanza.Definition = e.AttributeValue;
                            break;
                        }
                    case "namespace":
                        {
                            _currentStanza.NameSpace = e.AttributeValue;
                            break;
                        }
                    default:
                        {
                            _currentStanza.OboTagValuePairs.Add(new KeyValuePair<string, string>(e.Attribute, e.AttributeValue));
                            _currentStanza.Attributes.Add(new TermAttribute(e.Attribute, e.AttributeValue));
                            break;
                        }

                }
            }
        }

        private SimpleOboTerm _currentStanza;
        private void _oboReader_NewStanza(object sender, NewStanzaEventArgs e)
        {
            if (_currentStanza != null)
                this._terms.Add(_currentStanza);

            if (e.StanzaType == StanzaType.Term)
            {
                _currentStanza = new SimpleOboTerm("");
                _currentStanza.NameSpaceAlias = _nameSpaceAlias;
                _currentStanza.NameSpace = _nameSpace;
            }
            else
                _currentStanza = null;
        }
        #endregion

        #region IValidationSource Members

        public event EventHandler<ValidationMessage> Validation;
        protected virtual void OnValidation(ValidationMessage vm)
        {
            if (Validation != null)
                Validation(this, vm);
        }


        #endregion

        #region IXemlComponent Members

        public string Publisher
        {
            get { return "Max Planck Institute of Molecular Plant Physiology, Potsdam/Golm, Germany"; }
        }

        private Version _ver = new Version(1, 0, 0);
        public Version Version
        {
            get { return _ver; }
        }

        public string ComponentName
        {
            get { return "Standard Developmental Ontolgy for PO"; }
        }

        public string Author
        {
            get { return "Jan Hannemann"; }
        }

        public string Description
        {
            get { return "This is a standard handler for the PlantOntology developmental ontology."; }
        }

        public string Uri
        {
            get { return URI; }
        }

        public IXemlComponent Copy()
        {
            return new DevelopmentalOntologyHandler();
        }

        #endregion

        #region IHirachicalView Members
        private List<TermNode> _nodes;
        public IEnumerable<TermNode> Nodes
        {
            get { return _nodes; }
        }

        public TermNode FindNode(IOntologyTerm t)
        {
            return FindNode(t.TermId);
        }

        public TermNode FindNode(string termid)
        {
            foreach (var item in _nodes)
            {
                if (FindNode(item, termid) != null)
                    return FindNode(item, termid);
            }
            return null;
        }

        private TermNode FindNode(TermNode start, string termid)
        {
            TermNode ret = null;
            if (start.Term.TermId == termid)
                ret = start;
            else
                foreach (var item in start.Childs)
                {
                    if (FindNode(item.Relation, termid) != null)
                        ret = FindNode(item.Relation, termid);
                }

            return ret;
        }
        #endregion


        public event LoadedEventHandler OnLoaded;
    }
}
